package com.bestv.search.engine.command;

import com.bestv.search.common.dao.SearchFieldInfoDao;
import com.bestv.search.common.model.SearchFieldInfo;
import com.bestv.search.common.util.QueryStringProcess;
import com.bestv.search.engine.cache.KeySubjectFirstLevelCache;
import com.bestv.search.engine.cache.KeySubjectFirstLevelCache.EntityValue;
import com.bestv.search.engine.compass.extend.CategoryTopDocCollector;
import net.paoding.analysis.analyzer.PaodingAnalyzer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.FieldSelectorResult;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.*;
import org.compass.core.Compass;
import org.compass.core.lucene.engine.manager.DefaultLuceneSearchEngineIndexManager;
import org.compass.core.lucene.engine.manager.IndexHoldersCache;
import org.dom4j.Node;

import java.io.IOException;
import java.text.CollationKey;
import java.text.Collator;
import java.util.*;

public final class LuceneSearchCommand {

    protected static final Log log = LogFactory.getLog(LuceneSearchCommand.class);

    static IndexHoldersCache indexCaches = null;
    static boolean shouldReopenKey = true;
    static boolean shouldReopenInfo = true;
    static IndexSearcher indexSearcher = null;
    static IndexSearcher keySearcher = null;
    static String[] searchFields = null;
    static Map<String, Float> fieldsBoost = null;
    static SearchFieldInfoDao sfiDao = null;
    static Thread t = null;
    private static LuceneSearchCommand singletonLuceneSearchCommand;
    private static boolean isWarmed = false;
    private static boolean stop = false;
    private String sortType;

    private LuceneSearchCommand() {
    }

    public static LuceneSearchCommand getSingletonLuceneSearchCommand() {
        if (singletonLuceneSearchCommand == null) {
            singletonLuceneSearchCommand = new LuceneSearchCommand();
            log.info("</SingletonLuceneSearchCommand is initialized>!");
        }
        return singletonLuceneSearchCommand;
    }

    public static void initStaticFields(Compass compass, SearchFieldInfoDao sfDao) {
        log.info("<intializing static properties for LuceneSearchCommand>.....");
        LuceneSearchCommand.indexCaches = ((DefaultLuceneSearchEngineIndexManager) compass
                .getSearchEngineIndexManager()).getIndexHoldersCache();
        LuceneSearchCommand.sfiDao = sfDao;
        LuceneSearchCommand.initSearchFields();
        LuceneSearchCommand.initSearchers();
        if (singletonLuceneSearchCommand == null) {
            singletonLuceneSearchCommand = new LuceneSearchCommand();
            log.info("<SingletonLuceneSearchCommand is initialized>!");
        }
        if (LuceneSearchCommand.t == null) {
            LuceneSearchCommand.t = new Thread(singletonLuceneSearchCommand.new SearchFieldsConfigRunnable());
            t.start();
        }
        log.info("the searchFieldsConfig thread is started...");
        log.info("<static properties of LuceneSearchCommand initialized> !");
    }

    public static void initSearchFields() {
        log.info("Initializing the searchFieldInfos.....");
        List<SearchFieldInfo> list = sfiDao.getAll();
        if (list == null || list.size() == 0)
            return;
        Set<SearchFieldInfo> sfSet = new HashSet<SearchFieldInfo>(list);
        String[] searchFieldsTemp = new String[sfSet.size()];
        Map<String, Float> fieldsBoostTemp = new HashMap<String, Float>(sfSet.size());
        log.info("searchFieldInfos.size : " + sfSet.size());
        if (searchFields == null)
            searchFields = new String[sfSet.size()];
        if (fieldsBoost == null)
            fieldsBoost = new HashMap<String, Float>(sfSet.size());
        int i = 0;
        for (SearchFieldInfo sf : sfSet) {
            fieldsBoostTemp.put(sf.getName(), sf.getBoost());
            searchFieldsTemp[i++] = sf.getName();
            log.debug(sf);
        }
        fieldsBoost = fieldsBoostTemp;
        searchFields = searchFieldsTemp;
        log.info("the searchFieldInfos intialized !");
    }

    public static void initSearchers() {
        log.info("    initializing the indexSearcher and keySearcher......");
        indexCaches.refreshCache();
        indexSearcher = indexCaches.getHolder("info").getIndexSearcher();
        keySearcher = indexCaches.getHolder("keysubject").getIndexSearcher();
        log.info("the indexSearcher and keySearcher initialized!");
    }

    public static IndexSearcher refreshIndexCache(String subIndex) {
        log.info("refresh " + subIndex + ".....");
        indexCaches.refreshCache(subIndex);
        return indexCaches.getHolder(subIndex).getIndexSearcher();
    }

    public static void destory() {
        log.info("the searchFieldsConfig thread is stopping....");
        stop = true;
        t.interrupt();
        try {
            indexSearcher.getIndexReader().close();
            keySearcher.getIndexReader().close();
            indexCaches.clearCache();
            KeySubjectFirstLevelCache.stop();
        } catch (IOException e) {
            log.error(e);
        } finally {
            fieldsBoost.clear();
            indexSearcher = null;
            keySearcher = null;
            indexCaches = null;
            sfiDao = null;
            searchFields = null;
            fieldsBoost = null;
            singletonLuceneSearchCommand = null;
            KeySubjectFirstLevelCache.getFirstLevelSubjectCache().clear();
            KeySubjectFirstLevelCache.setFirstLevelSubjectCache(null);
            KeySubjectFirstLevelCache.getSecondLevelSubjectCache().clear();
            KeySubjectFirstLevelCache.setSecondLevelSubjectCache(null);
        }
    }

    public static boolean isWarmed() {
        return isWarmed;
    }

    public static void setWarmed(boolean isWarmed) {
        LuceneSearchCommand.isWarmed = isWarmed;
    }

    public static IndexSearcher getIndexSearcher() {
        if (indexSearcher == null) {
            indexCaches.refreshCache("info");
            indexSearcher = indexCaches.getHolder("info").getIndexSearcher();
            return indexSearcher;
        }
        return indexSearcher;
    }

    public static void setIndexSearcher(IndexSearcher searcher) {
        LuceneSearchCommand.indexSearcher = searcher;
    }

    public static boolean isShouldReopenKey() {
        return shouldReopenKey;
    }

    public static void setShouldReopenKey(boolean shouldReopenKey) {
        LuceneSearchCommand.shouldReopenKey = shouldReopenKey;
    }

    public static boolean isShouldReopenInfo() {
        return shouldReopenInfo;
    }

    public static void setShouldReopenInfo(boolean shouldReopenInfo) {
        LuceneSearchCommand.shouldReopenInfo = shouldReopenInfo;
    }

    public static IndexSearcher getKeySearcher() {
        if (keySearcher == null) {
            indexCaches.refreshCache("keysubject");
            keySearcher = indexCaches.getHolder("keysubject").getIndexSearcher();
            return keySearcher;
        }
        return keySearcher;
    }

    public static void setKeySearcher(IndexSearcher searcher) {
        LuceneSearchCommand.keySearcher = searcher;
    }

    public static Map<String, Float> getFieldsBoost() {
        return fieldsBoost;
    }

    public String getSortType() {
        return sortType;
    }

    public void setSortType(String sortType) {
        this.sortType = sortType;
    }

    private String xmlPiece(String code, String message) {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><error><code>" + code + "</code><message>" + message
                + "</message></error>";
    }

    @SuppressWarnings("unchecked")
    public final String search(org.dom4j.Document doc, String cmd) {
        if (getIndexSearcher() == null || getKeySearcher() == null) {
            log.error("The searchers are null!");
            return null;
        }
        String keywords;
        String[] filterFields = null, filterValues = null;
        int start = 0, limit = 10;

        Node keyNode = doc.selectSingleNode("/request/keywords");
        if (keyNode == null)
            return xmlPiece("1", "请求消息格式错误");
        keywords = keyNode.getText().trim();
        if ("".equals(keywords))
            return xmlPiece("1", "请求消息格式错误");
        Node startNode = doc.selectSingleNode("/request/start");
        if (startNode != null) {
            try {
                start = Integer.valueOf(startNode.getText());
            } catch (Exception e) {
                log.warn("Parse start node to integer error!");
                start = 0;
            }
        }
        if (start < 0)
            start = 0;
        Node limitNode = doc.selectSingleNode("/request/limit");
        if (limitNode != null) {
            try {
                limit = Integer.valueOf(limitNode.getText());
            } catch (Exception e) {
                log.warn("Parse limit node to integer error!");
                limit = 10;
            }
        }
        if (limit < 0)
            limit = 10;
        List<Node> filterNodes = doc.selectNodes("/request/filter");
        if (filterNodes != null && filterNodes.size() > 0) {
            int size = filterNodes.size();
            filterFields = new String[size];
            filterValues = new String[size];
            for (int i = 0; i < size; ++i) {
                Node filter = filterNodes.get(i);
                filterFields[i] = filter.valueOf("./field").trim();
                filterValues[i] = filter.valueOf("./value").trim();
                if ("".equals(filterFields[i]) || "".equals(filterValues[i]))
                    return xmlPiece("1", "请求消息格式错误");
            }
        }

        Query q = buildQuery(keywords, filterFields, filterValues);
        return search(q, getSortType(), start, limit, cmd);
    }

    private final Query buildQuery(String keywords, String[] filterFields, String[] filterValues) {
        String bizTypesStr = null;
        Set<EntityValue> keySubjects = new HashSet<EntityValue>();
        Query q = null;
        if (filterFields == null) {
            String[] keys = keywords.split("[ ]+");
            for (String keyName : keys) {
                Set<EntityValue> evs = KeySubjectFirstLevelCache.getEntityFromHeap(keyName);
                if (evs != null)
                    keySubjects.addAll(evs);
            }
        } else {
            String[] bizTypes = null;
            for (int i = 0; i < filterFields.length; ++i) {
                if ("bizType".equals(filterFields[i])) {
                    bizTypes = filterValues[i].split("[ ]+");
                    bizTypesStr = filterValues[i];
                    break;
                }
            }
            if (bizTypes == null) {
                String[] keyNames = keywords.split("[ ]+");
                for (String keyName : keyNames) {
                    Set<EntityValue> evs = KeySubjectFirstLevelCache.getEntityFromHeap(keyName);
                    if (evs != null)
                        keySubjects.addAll(evs);
                }
            } else {
                String[] keyNames = keywords.split("[ ]+");
                for (String keyName : keyNames) {
                    for (String bizType : bizTypes) {
                        EntityValue ev = KeySubjectFirstLevelCache.getEntityFromHeap(keyName, bizType);
                        if (ev != null) {
                            keySubjects.add(ev);
                        }
                    }
                }
            }
        }
        try {
            q = buildQuery(keywords, bizTypesStr, keySubjects, filterFields, filterValues);
        } catch (ParseException e) {
            log.error(e);
        }
        return q;
    }

    @SuppressWarnings("serial")
    public void warm() {
        if (isWarmed())
            return;
        if (indexSearcher == null || keySearcher == null) {
            log.error("indexSearcher or keySearcher is not initialized,please check the program...");
        } else {
            try {
                log.info("warming the indexSearcher and keySearcher....");
                TopDocs tds = keySearcher.search(new TermQuery(new Term("keysubjectname", "世博历史")), null, 100);
                int i = tds.totalHits;
                Document keyDoc = null;
                if (i > 0) {
                    ScoreDoc[] scoreDocs = tds.scoreDocs;
                    keyDoc = keySearcher.doc(scoreDocs[0].doc, new FieldSelector() {
                        public FieldSelectorResult accept(String fieldName) {
                            if (fieldName.equals("keysubjectname") || fieldName.equals("keysubject_keywords")
                                    || fieldName.equals("p_type"))
                                return FieldSelectorResult.LOAD;
                            return FieldSelectorResult.NO_LOAD;
                        }
                    });
                }
                String keywords = "世博 历史 NBA 中国";
                String p_type = "新闻";
                if (keyDoc != null) {
                    keywords = keyDoc.get("keysubject_keywords");
                    p_type = keyDoc.get("p_type");
                }
                log.info("keywords: " + keywords);
                log.info("p_type：" + p_type);
                BooleanQuery bool = new BooleanQuery();
                QueryParser pq = new QueryParser("keyword", new StandardAnalyzer());
                bool.add(pq.parse(keywords), BooleanClause.Occur.MUST);
                Term term = new Term("p_type", p_type);
                TermQuery tq = new TermQuery(term);
                bool.add(tq, BooleanClause.Occur.MUST);
                log.info(bool.toString());
                TopDocs topDocs = getIndexSearcher().search(bool, null, 5000);
                int total = topDocs.totalHits;
                log.info(total + " document is hitting !");
                ScoreDoc[] sds = topDocs.scoreDocs;
                for (ScoreDoc sd : sds) {
                    Document d = getIndexSearcher().doc(sd.doc, new FieldSelector() {
                        public FieldSelectorResult accept(String fieldName) {
                            if (fieldName.equals("name") || fieldName.equals("keyword") || fieldName.equals("p_type"))
                                return FieldSelectorResult.LOAD;
                            return FieldSelectorResult.NO_LOAD;
                        }
                    });
                    log.info(d);
                }
                setWarmed(true);
                log.info("indexSearcher and keySearcher warmed !");
            } catch (IOException e) {
                log.error(e);
            } catch (ParseException e) {
                log.error(e);
            }
        }
    }

    /*
     * keywords is a string maybe "" bizTypes is a string maybe null keySubjects
     * is a set,can be null or 0 size set filterFields/filterValues are string[]
     * and can be null;if they are not null,their length >= 1 and
     * filterFields[i]、filterValues[i] can not be "" string
     */
    private Query buildQuery(String keywords, String bizTypes, Set<EntityValue> keySubjects, String[] filterFields,
                             String[] filterValues) throws ParseException {
        boolean isBizTypeNotNull = !(bizTypes == null);
        BooleanQuery bool = new BooleanQuery();
        Analyzer sal = new PaodingAnalyzer();
        MultiFieldQueryParser chineseQp = new MultiFieldQueryParser(getSearchFields(), sal, getFieldBoosts());
        if (keySubjects != null && keySubjects.size() > 0) {
            Iterator<EntityValue> it = keySubjects.iterator();
            while (it.hasNext()) {
                BooleanQuery bq = new BooleanQuery();
                EntityValue ev = it.next();
                Query evKeyQuery = chineseQp.parse(QueryStringProcess.replaceAllIllegleChar(ev.getKeywords()));
                bq.add(evKeyQuery, BooleanClause.Occur.MUST);
                if (ev.getMust() != null && !ev.getMust().matches("[ ]*")) {
                    String[] musts = QueryStringProcess.replaceAllIllegleChar(ev.getMust()).split("[ ]+");
                    for (String must : musts) {
                        Query mustQuery = chineseQp.parse(must);
                        bq.add(mustQuery, BooleanClause.Occur.MUST);
                    }
                }
                if (!ev.getBizType().matches("[ ]*")) {
                    Query bizTypeQuery = new TermQuery(new Term("p_type", QueryStringProcess.replaceAllIllegleChar(ev
                            .getBizType())));
                    bq.add(bizTypeQuery, BooleanClause.Occur.MUST);
                }
                bool.add(bq, BooleanClause.Occur.SHOULD);
            }
        }
        if (!keywords.matches("[ ]*")) {
            keywords = QueryStringProcess.replaceAllIllegleChar(keywords);
            Query keyQuery = chineseQp.parse(keywords);
            if (isBizTypeNotNull) {
                BooleanQuery bq = new BooleanQuery();
                bq.add(keyQuery, BooleanClause.Occur.MUST);
                if (!bizTypes.matches("[ ]*")) {
                    QueryParser termParserQuery = new QueryParser("p_type", new WhitespaceAnalyzer());
                    Query bizTypeQuery = termParserQuery.parse(QueryStringProcess.replaceAllIllegleChar(bizTypes));
                    bq.add(bizTypeQuery, BooleanClause.Occur.MUST);
                }
                bool.add(bq, BooleanClause.Occur.SHOULD);
            } else {
                bool.add(keyQuery, BooleanClause.Occur.SHOULD);
            }
        }
        if (filterFields != null) {
            if (filterFields.length == 1 && "bizType".equals(filterFields[0]))
                return bool;
            BooleanQuery otherBool = new BooleanQuery();
            otherBool.add(bool, BooleanClause.Occur.MUST);
            for (int k = 0; k < filterFields.length; ++k) {
                if ("bizType".equals(filterFields[k]))
                    continue;
                // filters which don't refer to type range
                if (filterValues[k].indexOf("TO") < 0) {
                    QueryParser filterParser = new QueryParser(filterFields[k], sal);
                    Query filterQuery = filterParser.parse(filterValues[k]);
                    otherBool.add(filterQuery, BooleanClause.Occur.MUST);
                    continue;
                }
                // filters refering to type range
                Query rangQuery = null;
                String[] uts = filterValues[k].split("TO");
                if (uts.length == 2) {
                    Term utf = "min".equals(uts[0].trim()) ? null : new Term("updateTimeMillis", uts[0].replaceAll(
                            "[- :]+", ""));
                    Term utt = "max".equals(uts[1].trim()) ? null : new Term("updateTimeMillis", uts[1].replaceAll(
                            "[- :]+", ""));
                    if (utf != null || utt != null)
                        rangQuery = new RangeQuery(utf, utt, true, new Collator() {
                            public int compare(String source, String target) {
                                long s = Long.valueOf(source);
                                long t = Long.valueOf(target);
                                return s > t ? 1 : (s == t ? 0 : -1);
                            }

                            public CollationKey getCollationKey(String source) {
                                return null;
                            }

                            public int hashCode() {
                                return 0;
                            }
                        });
                }
                if (rangQuery != null) {
                    otherBool.add(rangQuery, BooleanClause.Occur.MUST);
                }
            }
            return otherBool;
        }
        return bool;
    }

    private final void appendDoc(StringBuffer sb, Document doc, int index, float score) {
        sb.append("<record>");
        sb.append("<index>");
        sb.append(index);
        sb.append("</index>");
        sb.append("<type>");
        sb.append(doc.get("type"));
        sb.append("</type>");
        sb.append("<code>");
        sb.append(doc.get("code"));
        sb.append("</code>");
        sb.append("<name>");
        sb.append(doc.get("name"));
        sb.append("</name>");
        sb.append("<bizType>");
        sb.append(doc.get("p_type"));
        sb.append("</bizType>");
        String startTime = doc.get("startTime");
        // if the doc refers to a schedule
        if (startTime != null && !"".equals(startTime)) {
            sb.append("<startTime>");
            sb.append(startTime);
            sb.append("</startTime>");
        }
        sb.append("</record>");
    }

    private final String search(Query q, String sortType, int start, int limit, String cmd) {
        if ("time".equals(sortType)) {
            Sort sort = new Sort(new SortField("updateTimeMillis", SortField.LONG, true));
            TopFieldDocs collector;
            int firstCount = 0;
            ScoreDoc[] sds_0 = new ScoreDoc[0];
            try {
                collector = getIndexSearcher().search(q, null, start + limit, sort);
                firstCount = collector.totalHits;
                sds_0 = collector.scoreDocs;
            } catch (IOException e) {
                log.error(e);
            }
            log.debug("Count: " + firstCount);
            log.debug("sds_0.length: " + sds_0.length);
            FieldSelector fs = new FieldSelector() {
                private static final long serialVersionUID = 4866747828197303004L;

                public FieldSelectorResult accept(String fieldName) {
                    if ("name".equals(fieldName) || "type".equals(fieldName) || "code".equals(fieldName)
                            || "p_type".equals(fieldName) || "startTime".equals(fieldName))
                        return FieldSelectorResult.LAZY_LOAD;
                    return FieldSelectorResult.NO_LOAD;
                }
            };
            int totalHits = firstCount;
            StringBuffer result = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?><result><count>");
            result.append(totalHits);
            result.append("</count>");
            int sds_0_ToNum = firstCount > start + limit ? start + limit : firstCount;
            int i = start;
            for (; i < sds_0_ToNum; ) {
                try {
                    float score = sds_0[i].score;
                    Document doc = getIndexSearcher().doc(sds_0[i].doc, fs);
                    appendDoc(result, doc, i++, score);
                } catch (CorruptIndexException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            result.append("</result>");
            return result.toString();
        } else if ("score".equals(sortType)) {
            if ("searchCategory".equalsIgnoreCase(cmd))
                return searchAndReturnCategoryCountResults(q, start, limit);
            return search(q, start, limit);
        }
        // else if("default".equals(sortType)){ extends here}
        return search(q, start, limit);
    }

    @SuppressWarnings("serial")
    private final String search(Query q, int start, int limit) {
        log.debug(q.toString());
        TopDocCollector collector = new TopDocCollector(start + limit);
        try {
            getIndexSearcher().search(q, collector);
        } catch (IOException e) {
            log.error(e);
        }
        int firstCount = collector.getTotalHits();
        ScoreDoc[] sds_0 = collector.topDocs().scoreDocs;
        log.debug("Count: " + firstCount);
        log.debug("sds_0.length: " + sds_0.length);

        FieldSelector fs = new FieldSelector() {
            public FieldSelectorResult accept(String fieldName) {
                if ("name".equals(fieldName) || "type".equals(fieldName) || "code".equals(fieldName)
                        || "p_type".equals(fieldName) || "startTime".equals(fieldName))
                    return FieldSelectorResult.LAZY_LOAD;
                return FieldSelectorResult.NO_LOAD;
            }
        };
        int totalHits = firstCount;
        StringBuffer result = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?><result><count>");
        result.append(totalHits);
        result.append("</count>");
        int sds_0_ToNum = firstCount > start + limit ? start + limit : firstCount;
        int i = start;
        for (; i < sds_0_ToNum; ) {
            try {
                float score = sds_0[i].score;
                Document doc = getIndexSearcher().doc(sds_0[i].doc, fs);
                appendDoc(result, doc, i++, score);
            } catch (CorruptIndexException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        result.append("</result>");
        return result.toString();
    }

    private final String searchAndReturnCategoryCountResults(Query q, int start, int limit) {
        log.debug(q.toString());
        CategoryTopDocCollector collector = new CategoryTopDocCollector(start + limit, getIndexSearcher());
        try {
            getIndexSearcher().search(q, collector);
        } catch (IOException e) {
            log.error(e);
        }
        int firstCount = collector.getTotalHits();
        ScoreDoc[] sds_0 = collector.topDocs().scoreDocs;
        log.debug("Count: " + firstCount);
        log.debug("sds_0.length: " + sds_0.length);

        FieldSelector fs = new FieldSelector() {
            private static final long serialVersionUID = 3889718399181605070L;

            public FieldSelectorResult accept(String fieldName) {
                if ("name".equals(fieldName) || "type".equals(fieldName) || "code".equals(fieldName)
                        || "p_type".equals(fieldName) || "startTime".equals(fieldName))
                    return FieldSelectorResult.LAZY_LOAD;
                return FieldSelectorResult.NO_LOAD;
            }
        };
        int totalHits = firstCount;
        StringBuffer result = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?><result><count>");
        result.append(totalHits);
        result.append("</count><categories>");
        Map<String, Integer> categoryCount = collector.getCategoryCount();
        for (String category : categoryCount.keySet()) {
            result.append("<category><bizType>");
            result.append(category).append("</bizType><count>").append(categoryCount.get(category));
            result.append("</count></category>");
        }
        result.append("</categories><records>");
        int sds_0_ToNum = firstCount > start + limit ? start + limit : firstCount;
        int i = start;
        for (; i < sds_0_ToNum; ) {
            try {
                float score = sds_0[i].score;
                Document doc = getIndexSearcher().doc(sds_0[i].doc, fs);
                appendDoc(result, doc, i++, score);
            } catch (CorruptIndexException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        result.append("</records></result>");
        return result.toString();
    }

    protected Map<String, Float> getFieldBoosts() {
        if (fieldsBoost != null)
            return fieldsBoost;
        Map<String, Float> map = new HashMap<String, Float>();
        map.put("name", 3.0f);
        map.put("keyword", 2.5f);
        map.put("tag", 2.5f);
        return map;
    }

    protected String[] getSearchFields() {
        if (searchFields != null)
            return searchFields;
        return new String[]{"name", "keyword", "tag"};
    }

    private class SearchFieldsConfigRunnable implements Runnable {
        public void run() {
            while (true) {
                if (stop)
                    return;
                try {
                    Thread.sleep(5 * 60 * 1000);
                } catch (InterruptedException e) {
                    if (stop) {
                        log.info("the searchFieldsConfig thread is stopped !");
                        return;
                    }
                    e.printStackTrace();
                }
                initSearchFields();
            }
        }

    }
}
